//////////////////////////////////////////////////////////////////////////
//
// This file is a part of XORT project.
//
// PROJECT:         Xort compiler
// FILE:            utils.cpp
// AUTHOR:          Vladimir Gumenuk
// DATE:            21-Mar-2008
// MODIFICATIONS:
//
//
//     Copyright(c) 2014, Vladimir Gumenuk
//     All rights reserved.
//
//     Redistribution and use in source and binary forms, with or without modification, 
//     are permitted provided that the following conditions are met :
//
//     1. Redistributions of source code must retain the above copyright notice, 
//        this list of conditions and the following disclaimer.
//     2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and 
//        the following disclaimer in the documentation and / or other materials provided with the distribution.
//     3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse 
//        or promote products derived from this software without specific prior written permission.
//
//     THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 
//     ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 
//     OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL 
//     THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 
//     SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT 
//     OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
//     HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 
//     OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, 
//     EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
//
//////////////////////////////////////////////////////////////////////////

#include "xort.h"

string csDELIMITERS = "+-*/%=;!(),.><[]{}?:|&^~";

int x_isdigit( byte ch )
{
    return ( ch >= '0' && ch <= '9' ) ? 1 : 0;
}

dword x_ishexdigit( byte ch ) // returns the value of hexadecimal digit or 0 if it is not
{
    if( ch >= 'a' && ch <= 'f' ) {
        return ch - 'a' + 10;
    }
    if( ch >= 'A' && ch <= 'F' ) {
        return ch - 'A' + 10;
    }
    return 0;
}

dword x_getHexValue( byte ch )
{
    dword iVal = x_ishexdigit( ch );
    if( !iVal )
    {
        if( x_isdigit( ch ) )
        {
            iVal = ch - '0';
        }
    }

    return iVal;
}

int x_isalpha( byte c )
{
    return ( c == '_' || (c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z') ) ? 1 : 0;
}

int x_isalnum( byte c )
{
    return ( x_isdigit( c ) || x_isalpha( c ) );
}

int x_isdelim( byte c )
{
    if( csDELIMITERS.hasChr( c ) )
        return 1;
    return 0;
}

dword x_gettypesize( dword dwType )
{
    if( dwType == t_s1 || dwType == t_u1 )
        return 1;
    if( dwType == t_s2 || dwType == t_u2 )
        return 2;
    if( dwType == t_s4 || dwType == t_u4 || dwType == t_r4 || dwType == t_p )
        return 4;
    if( dwType == t_s8 || dwType == t_u8 || dwType == t_r8 )
        return 8;

    return 0;
}


int x_intarHasVal( INTARRAY ar, int iVal )
{
    dword i;
    i = 0;
    while( i < ar.count() ) {
        if( ar.get( i ) == iVal ) {
            return 1;
        }
        ++i;
    }
    return 0;
}

// returns the number of arguments that need to be formatted, 0 if none
int x_isfmtstr( StringRef csFmt ) 
{
    dword dwNumArgs = 0;
    dword i = 0;
    dword dwLen = csFmt.Length();

    while( i < dwLen )
    {
        byte ch = csFmt.CharAt( i );
        if( ch == '%' )
        {
            ++i;
            ch = csFmt.CharAt( i );
            if( ch == '@' ) 
            {
                ++dwNumArgs;
            }
        } else {
            ++i;
        }
    }

    return dwNumArgs;
}

byte x_lobyte( word w )
{
    return w & 0xFF;
}

byte x_hibyte( word w )
{
    return ( w >> 8 ) & 0xFF;
}

word x_loword( dword dw )
{
    return dw & 0xFFFF;
}

word x_hiword( dword dw )
{
    return ( dw >> 16 ) & 0xFFFF;
}

string x_type2str( byte bType )
{
    if( bType == t_s1 ) {
        return "s1";
    } else if( bType == t_u1 ) {
        return "u1";
    } else if( bType == t_s2 ) {
        return "s2";
    } else if( bType == t_u2 ) {
        return "u2";
    } else if( bType == t_s4 ) {
        return "s4";
    } else if( bType == t_u4 ) {
        return "u4";
    } else if( bType == t_s8 ) {
        return "s8";
    } else if( bType == t_u8 ) {
        return "u8";
    } else if( bType == t_r4 ) {
        return "r4";
    } else if( bType == t_r8 ) {
        return "r8";
    } else if( bType == t_p  ) {
        return "p";
    }
    return "UNKNOWN TYPE";
}

int x_convert_type( dword bType, XELEMENT x, XELEMENT y )
{
    if( bType == t_s1 ) {
        if( y.bType == t_s1 ) {
            x._s1 = y._s1;
        } else if( y.bType == t_u1 ) {
            x._s1 = y._u1;
        } else if( y.bType == t_s2 ) {
            x._s1 = y._s2;
        } else if( y.bType == t_u2 ) {
            x._s1 = y._u2;
        } else if( y.bType == t_s4 ) {
            x._s1 = y._s4;
        } else if( y.bType == t_u4 ) {
            x._s1 = y._u4;
        } else if( y.bType == t_s8 ) {
            x._s1 = y._s8;
        } else if( y.bType == t_u8 ) {
            x._s1 = y._u8;
        } else if( y.bType == t_r4 ) {
            x._s1 = y._r4;
        } else if( y.bType == t_r8 ) {
            x._s1 = y._r8;
        } else {
            return ERR_CONVERSION_NOTPOSSIBLE;
        }
    } else if( bType == t_u1 ) {
        if( y.bType == t_s1 ) {
            x._u1 = y._s1;
        } else if( y.bType == t_u1 ) {
            x._u1 = y._u1;
        } else if( y.bType == t_s2 ) {
            x._u1 = y._s2;
        } else if( y.bType == t_u2 ) {
            x._u1 = y._u2;
        } else if( y.bType == t_s4 ) {
            x._u1 = y._s4;
        } else if( y.bType == t_u4 ) {
            x._u1 = y._u4;
        } else if( y.bType == t_s8 ) {
            x._u1 = y._s8;
        } else if( y.bType == t_u8 ) {
            x._u1 = y._u8;
        } else if( y.bType == t_r4 ) {
            x._u1 = y._r4;
        } else if( y.bType == t_r8 ) {
            x._u1 = y._r8;
        } else {
            return ERR_CONVERSION_NOTPOSSIBLE;
        }
    } else if( bType == t_s2 ) {
        if( y.bType == t_s1 ) {
            x._s2 = y._s1;
        } else if( y.bType == t_u1 ) {
            x._s2 = y._u1;
        } else if( y.bType == t_s2 ) {
            x._s2 = y._s2;
        } else if( y.bType == t_u2 ) {
            x._s2 = y._u2;
        } else if( y.bType == t_s4 ) {
            x._s2 = y._s4;
        } else if( y.bType == t_u4 ) {
            x._s2 = y._u4;
        } else if( y.bType == t_s8 ) {
            x._s2 = y._s8;
        } else if( y.bType == t_u8 ) {
            x._s2 = y._u8;
        } else if( y.bType == t_r4 ) {
            x._s2 = y._r4;
        } else if( y.bType == t_r8 ) {
            x._s2 = y._r8;
        } else {
            return ERR_CONVERSION_NOTPOSSIBLE;
        }
    } else if( bType == t_u2 ) {
        if( y.bType == t_s1 ) {
            x._u2 = y._s1;
        } else if( y.bType == t_u1 ) {
            x._u2 = y._u1;
        } else if( y.bType == t_s2 ) {
            x._u2 = y._s2;
        } else if( y.bType == t_u2 ) {
            x._u2 = y._u2;
        } else if( y.bType == t_s4 ) {
            x._u2 = y._s4;
        } else if( y.bType == t_u4 ) {
            x._u2 = y._u4;
        } else if( y.bType == t_s8 ) {
            x._u2 = y._s8;
        } else if( y.bType == t_u8 ) {
            x._u2 = y._u8;
        } else if( y.bType == t_r4 ) {
            x._u2 = y._r4;
        } else if( y.bType == t_r8 ) {
            x._u2 = y._r8;
        } else {
            return ERR_CONVERSION_NOTPOSSIBLE;
        }
    } else if( bType == t_s4 ) {
        if( y.bType == t_s1 ) {
            x._s4 = y._s1;
        } else if( y.bType == t_u1 ) {
            x._s4 = y._u1;
        } else if( y.bType == t_s2 ) {
            x._s4 = y._s2;
        } else if( y.bType == t_u2 ) {
            x._s4 = y._u2;
        } else if( y.bType == t_s4 ) {
            x._s4 = y._s4;
        } else if( y.bType == t_u4 ) {
            x._s4 = y._u4;
        } else if( y.bType == t_s8 ) {
            x._s4 = y._s8;
        } else if( y.bType == t_u8 ) {
            x._s4 = y._u8;
        } else if( y.bType == t_r4 ) {
            x._s4 = y._r4;
        } else if( y.bType == t_r8 ) {
            x._s4 = y._r8;
        } else {
            return ERR_CONVERSION_NOTPOSSIBLE;
        }
    } else if( bType == t_u4 ) {
        if( y.bType == t_s1 ) {
            x._u4 = y._s1;
        } else if( y.bType == t_u1 ) {
            x._u4 = y._u1;
        } else if( y.bType == t_s2 ) {
            x._u4 = y._s2;
        } else if( y.bType == t_u2 ) {
            x._u4 = y._u2;
        } else if( y.bType == t_s4 ) {
            x._u4 = y._s4;
        } else if( y.bType == t_u4 ) {
            x._u4 = y._u4;
        } else if( y.bType == t_s8 ) {
            x._u4 = y._s8;
        } else if( y.bType == t_u8 ) {
            x._u4 = y._u8;
        } else if( y.bType == t_r4 ) {
            x._u4 = y._r4;
        } else if( y.bType == t_r8 ) {
            x._u4 = y._r8;
        } else {
            return ERR_CONVERSION_NOTPOSSIBLE;
        }
    } else if( bType == t_s8 ) {
        if( y.bType == t_s1 ) {
            x._s8 = y._s1;
        } else if( y.bType == t_u1 ) {
            x._s8 = y._u1;
        } else if( y.bType == t_s2 ) {
            x._s8 = y._s2;
        } else if( y.bType == t_u2 ) {
            x._s8 = y._u2;
        } else if( y.bType == t_s4 ) {
            x._s8 = y._s4;
        } else if( y.bType == t_u4 ) {
            x._s8 = y._u4;
        } else if( y.bType == t_s8 ) {
            x._s8 = y._s8;
        } else if( y.bType == t_u8 ) {
            x._s8 = y._u8;
        } else if( y.bType == t_r4 ) {
            x._s8 = y._r4;
        } else if( y.bType == t_r8 ) {
            x._s8 = y._r8;
        } else {
            return ERR_CONVERSION_NOTPOSSIBLE;
        }
    } else if( bType == t_u8 ) {
        if( y.bType == t_s1 ) {
            x._u8 = y._s1;
        } else if( y.bType == t_u1 ) {
            x._u8 = y._u1;
        } else if( y.bType == t_s2 ) {
            x._u8 = y._s2;
        } else if( y.bType == t_u2 ) {
            x._u8 = y._u2;
        } else if( y.bType == t_s4 ) {
            x._u8 = y._s4;
        } else if( y.bType == t_u4 ) {
            x._u8 = y._u4;
        } else if( y.bType == t_s8 ) {
            x._u8 = y._s8;
        } else if( y.bType == t_u8 ) {
            x._u8 = y._u8;
        } else if( y.bType == t_r4 ) {
            x._u8 = y._r4;
        } else if( y.bType == t_r8 ) {
            x._u8 = y._r8;
        } else {
            return ERR_CONVERSION_NOTPOSSIBLE;
        }
    } else if( bType == t_r4 ) {
        if( y.bType == t_s1 ) {
            x._r4 = y._s1;
        } else if( y.bType == t_u1 ) {
            x._r4 = y._u1;
        } else if( y.bType == t_s2 ) {
            x._r4 = y._s2;
        } else if( y.bType == t_u2 ) {
            x._r4 = y._u2;
        } else if( y.bType == t_s4 ) {
            x._r4 = y._s4;
        } else if( y.bType == t_u4 ) {
            x._r4 = y._u4;
        } else if( y.bType == t_s8 ) {
            x._r4 = y._s8;
        } else if( y.bType == t_u8 ) {
            x._r4 = y._u8;
        } else if( y.bType == t_r4 ) {
            x._r4 = y._r4;
        } else if( y.bType == t_r8 ) {
            x._r4 = y._r8;
        } else {
            return ERR_CONVERSION_NOTPOSSIBLE;
        }
    } else if( bType == t_r8 ) {
        if( y.bType == t_s1 ) {
            x._r8 = y._s1;
        } else if( y.bType == t_u1 ) {
            x._r8 = y._u1;
        } else if( y.bType == t_s2 ) {
            x._r8 = y._s2;
        } else if( y.bType == t_u2 ) {
            x._r8 = y._u2;
        } else if( y.bType == t_s4 ) {
            x._r8 = y._s4;
        } else if( y.bType == t_u4 ) {
            x._r8 = y._u4;
        } else if( y.bType == t_s8 ) {
            x._r8 = y._s8;
        } else if( y.bType == t_u8 ) {
            x._r8 = y._u8;
        } else if( y.bType == t_r4 ) {
            x._r8 = y._r4;
        } else if( y.bType == t_r8 ) {
            x._r8 = y._r8;
        } else {
            return ERR_CONVERSION_NOTPOSSIBLE;
        }
    } else if( bType >= t_p ) {
        if( y.bType == t_p && y._p == NULL ) { // special case for NULL pointer assignment
            x._p = NULL;
            x.bType = t_p;
            return XORT_OK;
        } else {
            return ERR_CONVERSION_NOTPOSSIBLE;
        }
    } else {
        return ERR_CONVERSION_NOTPOSSIBLE;
    }

    x.bType = bType;
    return XORT_OK;
}

void x_copyelement( XELEMENT dst, XELEMENT src )
{
    dst.bType   = src.bType;
    dst.bAccess = src.bAccess;
    dst._u8     = src._u8;
}

int x_isAssignOp( dword op )
{
    if( op == tk_assign || op == tk_assignminus || op == tk_assignplus || op == tk_assignmul || 
        op == tk_assigndiv || op == tk_assignmod || op == tk_assignshr || op == tk_assignshl || 
        op == tk_assignbinand || op == tk_assignxor || op == tk_assignbinor )
    {
        return TRUE;
    }

    return FALSE;
}

void x_copyCode( SymbolRef Dst, SymbolRef Src )
{
    dword i = 0;
    dword dwCodeCount = Src.m_Code.count();
    while( i < dwCodeCount ) {
        Dst.m_Code.add( Src.m_Code.get( i ) );
        ++i;
    }
}

byte x_code_from_tok( dword op )
{
    if( op == tk_assignminus ) {
        return p_sub;
    } else if( op == tk_assignplus ) {
        return p_add;
    } else if( op == tk_assignmul ) { 
        return p_mul;
    } else if( op == tk_assigndiv ) {
        return p_div;
    } else if( op == tk_assignmod ) {
        return p_mod;
    } else if( op == tk_assignshr ) {
        return p_shr;
    } else if( op == tk_assignshl ) {
        return p_shl;
    } else if( op == tk_assignbinand ) {
        return p_and;
    } else if( op == tk_assignxor ) {
        return p_xor;
    } else if( op == tk_assignbinor ) {
        return p_or;
    } else {
        return 0;
    }
}

dword x_tok_from_code( byte bCode )
{
    switch( bCode )
    {
    case p_or:  return tk_binor;      // |
    case p_xor: return tk_xor;        // ^
    case p_and: return tk_binand;     // &
    case p_shl: return tk_shiftleft;  // <<
    case p_shr: return tk_shiftright; // >>
    case p_add: return tk_plus;       // +
    case p_sub: return tk_minus;      // -
    case p_mul: return tk_mul;        // *
    case p_div: return tk_div;        // /
    case p_mod: return tk_mod;        // %
    default:
        return 0;
    }
}
//////////////////////////////////////////////////////////////////////////
// C-style versions are completely wrong in Xort:
// short x = ( b1 & 0xFF ) | ( (b2 & 0xFF) << 8 );
// now update:
short x_makeShort( char b1, char b2 )
{
    short x = b1 & 0x00FF;
    short x2 = b2 & 0x00FF;
    x |= ( x2 << 8 );
    return x;
}

word x_makeWord( byte b1, byte b2 )
{
    word x = b1 & 0x00FF;
    word x2 = b2 & 0x00FF;
    x |= ( x2 << 8 );
    return x;
}

int x_makeInt( short s1, short s2 )
{
    int x = s1 & 0x0000FFFF;
    int x2 = s2 & 0x0000FFFF;
    x |= ( x2 << 16 );
    return x;
}

dword x_makeDword( word w1, word w2 )
{
    dword x = w1 & 0x0000FFFF;
    dword x2 = w2 & 0x0000FFFF;
    x |= ( x2 << 16 );
    return x;
}

qlong x_makeLong( int s1, int s2 )
{
    qlong x = s1 & 0xFFFFFFFF;
    qlong x2 = s2 & 0xFFFFFFFF;
    x |= ( x2 << 32 );
    return x;
}

qword x_makeQword( dword w1, dword w2 )
{
    qword x = w1 & 0xFFFFFFFF;
    qword x2 = w2 & 0xFFFFFFFF;
    x |= ( x2 << 32 );
    return x;
}

int x_findAttribute( ATTRARRAY Attr, dword dwCode, StringRef strOut )
{
    dword i;
    dword dwCount = Attr.count();
    for( i = 0; i < dwCount; ++i )
    {
        AttributesRef at = Attr.get( i );
        if( MEMBER_PTR(at)dwAttrCode == dwCode )
        {
            strOut.Assign( MEMBER_PTR(at)csAttrValue );
            return TRUE;
        }
    }
    return FALSE;
}

int x_is_metainstruction( byte bCode )
{
    if( bCode == p_label || bCode == p_stkvar || bCode == p_endstkvar || bCode == p_begincall || bCode == p_endcall ||
        bCode == p_begin_if || bCode == p_end_if || bCode == p_beginret || bCode == p_endret || bCode == p_beginasgn ||
        bCode == p_endasgn || bCode == p_begswitch || bCode == p_endswitch || bCode == p_begcase || bCode == p_endcase ||
        bCode == p_beginarg || bCode == p_endarg )
    {
        return TRUE;
    }
    return FALSE;
}

#ifdef __XVM__

string xEscapeString( StringRef str ) // escape special characters
{
    string csRet;

    dword i;
    dword dwLen = str.Length();
    for( i = 0; i < dwLen; ++i )
    {
        byte ch = str.CharAt( i );
        switch( ch )
        {
        case 0x07: csRet.Add( "\\a" ); break;
        case 0x08: csRet.Add( "\\b" ); break;
        case 0x0C: csRet.Add( "\\f" ); break;
        case 0x0A: csRet.Add( "\\n" ); break;
        case 0x0D: csRet.Add( "\\r" ); break;
        case 0x09: csRet.Add( "\\t" ); break;
        case 0x0B: csRet.Add( "\\v" ); break;
        case 0x27: csRet.Add( "\\" ); break;
        case 0x22: csRet.Add( "\"" ); break;
        case 0x5C: csRet.Add( "\\" ); break;
        case 0x3F: csRet.Add( "\\?" ); break;
        case 0x00: csRet.Add( "\0" ); break;
        default: csRet.Append( ch ); break;
        }
    }

    return csRet;
}

string xFormatElementValue( XELEMENT x, dword dwRadix )
{
    string csOut;

    switch( x.bType )
    {
    case t_s1: csOut.Format( (dwRadix == 16) ? "0x%02X" : "%d", x._s1 ); break;
    case t_u1: csOut.Format( (dwRadix == 16) ? "0x%02X" : "%u", x._u1 ); break;
    case t_s2: csOut.Format( (dwRadix == 16) ? "0x%04X" : "%d", x._s2 ); break;
    case t_u2: csOut.Format( (dwRadix == 16) ? "0x%04X" : "%u", x._u2 ); break;
    case t_s4: csOut.Format( (dwRadix == 16) ? "0x%08X" : "%d", x._s4 ); break;
    case t_u4: csOut.Format( (dwRadix == 16) ? "0x%08X" : "%u", x._u4 ); break;
    case t_s8: csOut.Format( (dwRadix == 16) ? "%llX"   : "%lld", x._s8 ); break;
    case t_u8: csOut.Format( (dwRadix == 16) ? "%llX"   : "%llu", x._u8 ); break;
    case t_r4: csOut.Format( (dwRadix == 16) ? "%.6f"   : "%.6f", x._r4 ); break;
    case t_r8: csOut.Format( (dwRadix == 16) ? "%.12f"  : "%.12f", x._r8 ); break;
    case t_p : 
        if( x._p != NULL ) {
            dword dwType = ((xobject*)x._p)->GetType();
            if( dwType == t_string ) {
                string cStr = xEscapeString( *((string*)x._p) );
                csOut.Format( (dwRadix == 16) ? "0x%08X ('%s')" :  "%u ('%s')", x._p, cStr.GetBuffer() );
            } else {
                csOut.Format( (dwRadix == 16) ? "0x%08X (object)" : "%u (object)", x._p );
            }
        } else { 
            csOut.Format( (dwRadix == 16) ? "0x%08X" : "%u", x._p );
        }
        break;
    default:   csOut.Assign( "invalid" ); break;
    }

    return csOut;
}
#endif // #ifdef __XVM__
